/*
 * Copyright 2008 Zoundry LLC, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package org.zoundry.gaedt.core.extensions;

import java.io.InvalidClassException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.InvalidRegistryObjectException;
import org.eclipse.core.runtime.Platform;
import org.zoundry.gaedt.core.util.ZGaeUtil;

/**
 * A simple util class to aid in loading contributions to custom extension
 * points.
 */
public class ZGaeExtensionLoader
{
    /** Caches the list of extensions, indexed by extension point id. */
    private static Map<String, List> sExtensionCache = new HashMap();

    /**
     * Discovers all of the contributions made on the given extension point.
     * This method returns a List of instances of type 'aExtensionType'. Note
     * that this method automatically caches the List of extensions in a map
     * indexed by extension point id.
     *
     * @param aExtensionPointId
     * @param aExtensionType
     * @throws CoreException
     */
    public static List discoverContributors(String aExtensionPointId, Class aExtensionType)
    {
        return discoverContributors(aExtensionPointId, "class", aExtensionType); //$NON-NLS-1$
    }

    /**
     * Discovers all of the contributions made on the given extension point.
     * This method returns a List of instances of type 'aExtensionType'. Note
     * that this method automatically caches the List of extensions in a map
     * indexed by extension point id.
     *
     * @param aExtensionPointId
     * @param aAttributeName
     * @param aExtensionType
     * @throws CoreException
     */
    public static List discoverContributors(String aExtensionPointId, String aAttributeName,
            Class aExtensionType)
    {
        List contributors = sExtensionCache.get(aExtensionPointId);
        if (contributors == null)
        {
            contributors = new ArrayList();
            sExtensionCache.put(aExtensionPointId, contributors);

            IExtension[] extensions = getContributorExtensions(aExtensionPointId);
            for (int i = 0; i < extensions.length; i++)
            {
                IExtension extension = extensions[i];
                IConfigurationElement[] elements = extension.getConfigurationElements();
                for (IConfigurationElement element : elements)
                {
                    Object contributorImpl = createExecutableExtension(element, aAttributeName);
                    if (aExtensionType.isAssignableFrom(contributorImpl.getClass()))
                    {
                        contributors.add(contributorImpl);
                    }
                    else
                    {
                        String msg = "Contributed class ''{0}'', for extension/contribution ''{1}'' is incompatible with the type expected for the extension point ''{2}''.";
                        msg = MessageFormat.format(msg, contributorImpl.getClass().getName(),
                                extension.getUniqueIdentifier(), aExtensionPointId);
                        ZGaeUtil.logError(new InvalidClassException(msg));
                    }
                }
            }
        }

        // return a copy to prevent people from changing the original
        return new ArrayList(contributors);
    }

    /**
     * This method exists to wrap the {@link IConfigurationElement}'s
     * createExecutableExtension method so that we can rethrow the core
     * exception as a runtime exception.
     *
     * @param aElement
     * @param aAttributeName
     * @throws CoreException
     */
    private static Object createExecutableExtension(IConfigurationElement aElement, String aAttributeName)
    {
        try
        {
            return aElement.createExecutableExtension(aAttributeName);
        }
        catch (Throwable e)
        {
            throw new RuntimeException(e);
        }
    }

    /**
     * For specified extension point, get all plugins that extend to this
     * extension point.
     *
     * @param aExtensionPointId
     * @throws InvalidRegistryObjectException
     */
    public static IConfigurationElement[] getConfigElements(String aExtensionPointId)
            throws InvalidRegistryObjectException
    {
        IConfigurationElement[] configElements = new IConfigurationElement[0];

        IExtensionRegistry registry = Platform.getExtensionRegistry();
        if (registry != null)
        {
            IExtensionPoint extensionPoint = registry.getExtensionPoint(aExtensionPointId);
            if (extensionPoint != null)
                configElements = extensionPoint.getConfigurationElements();
            else
                configElements = new IConfigurationElement[0];
        }

        return configElements;
    }

    /**
     * Get all plugins at a specific extension point id.
     *
     * @param aExtensionPointId
     * @throws InvalidRegistryObjectException
     */
    public static IExtension[] getContributorExtensions(String aExtensionPointId)
            throws InvalidRegistryObjectException
    {
        IExtension[] extensions = null;

        IExtensionRegistry registry = Platform.getExtensionRegistry();
        IExtensionPoint extensionPoint = registry.getExtensionPoint(aExtensionPointId);
        if (extensionPoint != null)
            extensions = extensionPoint.getExtensions();
        else
            extensions = new IExtension[0];

        return extensions;
    }
}
